<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>给图片添加水印</title>
    <style>
      img {
        width: 100vw;
      }
    </style>
  </head>
  <body>
    <input type="file" id="fileUplodBox" />
    <img alt="原图" id="originPic" />
    <img alt="水印图" id="waterMark" />
  </body>
  <script>
    // 声明一个名为fileUplodBox的变量，该变量引用页面上ID为"fileUplodBox"的元素
    const fileUplodBox = document.getElementById("fileUplodBox");

    // 为fileUplodBox元素添加"change"事件监听器，当用户选择文件后触发该事件
    fileUplodBox.addEventListener("change", e => {
      // 获取用户选择的文件
      const file = e.target.files[0];

      // 调用dealFile函数处理用户选择的文件
      dealFile(file);
    });

    // 调用test函数，此函数的主要作用是将指定的图片URL转换为base64格式，并设置为页面的"waterMark"元素的src属性
    test();

    // 定义一个异步函数test
    async function test() {
      // 调用fillImgToImg函数，将imgLink变量的值作为参数，此函数的作用是将指定的图片URL转换为base64格式，并返回
      const img = await fillImgToImg(imgLink);

      // 将转换后的图片设置为页面的"waterMark"元素的src属性
      document.getElementById("waterMark").setAttribute("src", img);
    }

    // 定义一个异步函数dealFile
    async function dealFile(file) {
      // 调用fileToBase64Async函数，将用户选择的文件作为参数，此函数的作用是将用户选择的文件转换为base64格式，并返回
      const base64 = await fileToBase64Async(file);

      // 将转换后的图片设置为页面的"originPic"元素的src属性
      document.getElementById("originPic").setAttribute("src", base64);

      // 调用fillTextToImg函数，将转换后的图片的base64编码作为参数，此函数的作用是在图片上添加文字水印，并返回一个Promise对象
      const img = await fillTextToImg(base64);

      // 将添加了文字水印的图片设置为页面的"waterMark"元素的src属性
      document.getElementById("waterMark").setAttribute("src", img);
    }

    // 定义一个函数fileToBase64Async
    function fileToBase64Async(file) {
      // 返回一个新的Promise对象
      return new Promise((resolve, reject) => {
        // 创建一个FileReader对象
        let reader = new FileReader();

        // 用FileReader对象读取用户选择的文件
        reader.readAsDataURL(file);

        // 当文件读取完成后执行以下代码
        reader.onload = e => {
          // 将读取的文件内容（以base64格式编码）返回给调用者
          resolve(e.target.result);
        };
      });
    }

    // 定义一个函数fillTextToImg，用于在图片上添加文字水印
    function fillTextToImg(base64) {
      // 创建一个新的Image对象
      const img = new Image();

      // 将转换后的图片设置为新创建的Image对象的src属性
      img.src = base64;

      img.setAttribute("crossOrigin", "Anonymous"); // 设置图片的跨域属性，允许在canvas中绘制图片（如果图片来源于不同的域）
      return new Promise((resolve, reject) => {
        // 返回一个新的Promise对象，用于异步操作完成后返回结果给调用者。这里主要是为了在图片加载完成后进行绘制操作。如果直接在img.onload中返回结果，则会导致Promise对象在图片加载完成时才返回，而不是在调用fillTextToImg时立即返回。因此，我们需要一个Promise来正确地处理这个问题。同时，Promise的使用也使得代码更加清晰易懂。在Promise中，我们可以使用异步操作来执行复杂的逻辑。在执行异步操作时，我们可以使用async/await语法来简化代码。例如，我们可以使用async函数来包装一个异步操作，并使用await关键字来等待异步操作完成。当异步操作完成后，我们可以使用resolve()方法来将结果返回给调用者。如果异步操作失败，则可以使用reject()方法来返回错误信息。这里使用Promise而不是回调函数主要是为了使代码更加清晰易懂。通过使用Promise和async/await语法，我们可以避免回调地狱，使代码更加简洁易读。

        img.onload = () => {
          // 当图片加载完成后执行以下代码

          // 创建一个canvas元素
          const canvas = document.createElement("canvas");

          // 获取canvas的2d上下文
          const ctx = canvas.getContext("2d");

          // 设置canvas的宽度和高度
          canvas.width = img.width;
          canvas.height = img.height;

          // 在canvas中绘制图片
          ctx.fillRect(0, 0, canvas.width, canvas.height);
          ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

          // 获取上传时间和用户名称
          const remFontSize = canvas.width / 35; // 根据canvas宽度计算字体大小（以rem为单位的比例尺）
          ctx.font = "bolder " + remFontSize + "px Verdana"; // 设置字体样式和大小
          ctx.textAlign = "center"; // 设置文本对齐方式为居中
          ctx.strokeStyle = "#fff"; // 设置文本描边颜色为白色
          const uploadTime = new Date(); // 获取当前上传时间
          const name = "@涵崽"; // 获取用户名称（这里假设上传图片的用户名称是@涵崽）
          const spaceH = remFontSize * 0.3; // 计算水平间距（以rem为单位的比例尺）
          ctx.strokeText(
            name,
            canvas.width / 2,
            canvas.height - remFontSize - spaceH
          ); // 在图片中央绘制文本水印（包括上传时间和用户名称）
          resolve(canvas.toDataURL("image/jpeg")); // 将添加了文本水印的图片以data URL的形式返回给调用者，并设置为页面的"waterMark"元素的src属性。data URL是一种将二进制数据编码为URL的方法，可以用于在网页上显示图像等资源。这里将添加了文本水印的图片转换为data URL后，直接将其设置为页面的"waterMark"元素的src属性即可显示出来。同时，resolve()方法用于将结果返回给Promise的调用者，表示异步操作已完成并返回结果。如果异步操作失败，则可以使用reject()方法返回错误信息。这里使用Promise而不是回调函数主要是为了使代码更加清晰易懂。通过使用Promise和async/await语法，我们可以避免回调地狱，使代码更加简洁易读。
        };
      });
    }
  </script>
</html>
